/*
 * @file          device.c
 * @author        Gavin Liu
 * @version       v0.1
 * @brief         a implementation of device management,it is easy to port rtos environment.    
 *                
 * Change Logs:
 * Date           Author       Notes
 * 2016-05-22     Gavin Liu    release 
 */

#include "device.h"
#include "string.h"

static struct lt_dev_s* device_address_list[LT_DEV_NUM_MAX] = { 0 };

void lt_dev_init(void)
{
    
    memset(device_address_list, 0x00, sizeof(device_address_list));
    
}

int32_t lt_dev_get_num(void)
{
    int valid;
    int i;

    
    for(i = 0,valid = 0; i < LT_DEV_NUM_MAX; i++){
        if(device_address_list[i] != NULL){
            valid++;
        }
    }
    

    return valid;
}

int32_t lt_dev_get_info(struct lt_dev_info_s *info,int num)
{
    int32_t i;
    int32_t j;

    
    for(i = 0,j = 0; i < LT_DEV_NUM_MAX; i++){
        if(device_address_list[i] != NULL){
            memcpy(info[j].name,device_address_list[i]->name,LT_MAX_OBJ_NAME_LEN);
            info[j].flag       = device_address_list[i]->flag;
            info[j].open_flag  = device_address_list[i]->open_flag;
            info[j].ref_count  = device_address_list[i]->ref_count;
            j++;

            if(j >= num){
                break;
            }
        }
    }
    

    return j;
}

int32_t lt_dev_find(const char* name)
{
    int32_t i;
    int32_t ret = -1;

    
    for (i = 0; i < LT_DEV_NUM_MAX; i++) {
        if (device_address_list[i] != NULL) {
            if (strcmp(name, device_address_list[i]->name) == 0) {
                ret = i;
                break;
            }
        }
    }
    
    return ret;
}

int32_t lt_dev_register(struct lt_dev_s* dev, const char* name, uint16_t flags)
{
    int32_t i;
    int32_t ret = -1;
    int32_t name_length = 0;

    if (lt_dev_find(name) >= 0) {
        return -1;
    }

    
    for (i = 0; i < LT_DEV_NUM_MAX; i++) {
        if (device_address_list[i] == NULL) {
            dev->flag = flags;
            device_address_list[i] = dev;
            name_length = strlen(name);
            name_length = name_length > LT_MAX_OBJ_NAME_LEN ? LT_MAX_OBJ_NAME_LEN : name_length;
            strncpy(device_address_list[i]->name, name, name_length);
            ret = 0;
            break;
        }
    }
    
    return ret;
}

int32_t lt_dev_unregister(struct lt_dev_s* dev)
{
    int32_t i;
    int32_t ret = -1;

    
    for (i = 0; i < LT_DEV_NUM_MAX; i++) {
        if (device_address_list[i] != NULL) {
            if (device_address_list[i] == dev) {
                device_address_list[i] = NULL;
                ret = 0;
                break;
            }
        }
    }
    
    return ret;
}

int32_t lt_dev_open(char* name, uint16_t oflag)
{
    struct lt_dev_s* dev = NULL;
    int32_t ret = 0;
    int32_t fd = -1;

    fd = lt_dev_find(name);

    if (fd < 0) {
        return -100;
    }

    
    dev = device_address_list[fd];
    
    if(dev->ref_count == 0){
        if (dev->init != NULL) {
            ret = dev->init(dev);
            if (ret != 0) {
                return ret;
            }
        }else{
            return -100;
        }
    }else if (dev->ref_count > 0){
        if ((dev->flag & LT_DEVICE_FLAG_STANDALONE)) {
            return -200;
        }
    }

    if (dev->open != NULL) {
        ret = dev->open(dev, oflag);
        if (ret == 0) {
            dev->ref_count++;
            dev->open_flag |= oflag;
            return fd;
        } else {
            return -100;
        }
    } else {
        return -100;
    }

    return -100;
}

int32_t lt_dev_close(int32_t fd)
{
    int32_t ret = 0;
    struct lt_dev_s* dev = NULL;

    if (fd >= LT_MAX_OBJ_NAME_LEN || fd < 0) {
        return -1;
    }

    dev = device_address_list[fd];

    if (dev == NULL) {
        return -1;
    }

    if (dev->close != NULL) {
        ret = dev->close(dev); 
    }
    
    if (dev->ref_count > 0) {
        dev->ref_count--;
    }

    if (dev->ref_count == 0) {
        if(dev->deinit != NULL){
            dev->deinit(dev);
        }
        dev->open_flag = 0x00;
    } else {
        ret = 0;
    }

    return ret;
}

int32_t lt_dev_read(int32_t fd, void* buffer, int32_t size, int32_t pos)
{
    int32_t ret;
    struct lt_dev_s* dev = NULL;

    if (fd >= LT_DEV_NUM_MAX || fd < 0) {
        return -1;
    }
    
    dev = device_address_list[fd];

    if (dev == NULL) {
        return -1;
    }

    if (dev->ref_count > 0) {
        if (dev->read != NULL) {
            ret = dev->read(dev, buffer, size, pos);
        }
    } else {
        ret = -1;
    }

    return ret;
}

int32_t lt_dev_write(int32_t fd, const void* buffer, int32_t size, int32_t pos)
{
    int32_t ret;
    struct lt_dev_s* dev = NULL;

    if (fd >= LT_DEV_NUM_MAX || fd < 0) {
        return -1;
    }

    dev = device_address_list[fd];

    if (dev == NULL) {
        return -1;
    }

    if (dev->ref_count > 0) {
        if (dev->write != NULL) {
            ret = dev->write(dev, buffer, size, pos);
        }
    } else {
        ret = -1;
    }

    return ret;
}

int32_t lt_dev_ioctl(int32_t fd, uint32_t cmd, void* args)
{
    int32_t ret;
    struct lt_dev_s* dev = NULL;

    if (fd >= LT_DEV_NUM_MAX || fd < 0) {
        return -1;
    }

    
    dev = device_address_list[fd];
    

    if (dev == NULL) {
        return -1;
    }

    if (dev->ref_count > 0) {
        if (dev->ioctl != NULL) {
            ret = dev->ioctl(dev, cmd, args);
        }
    } else {
        ret = -1;
    }

    return ret;
}
